/**
 * @file calendar.c
 * @brief 农历与公历转换及日历显示功能的实现文件
 * @details 包含农历/公历互转、月历打印、节气计算、节日显示等功能的具体实现。
 */

#include <stdio.h>
#include <stdbool.h>
#include "windows.h"
#include"..\..\include\console\console_color.h"
#include "..\..\include\calendar\calendar.h"
#include "..\..\include\calendar\calendar_table.h"
#include"..\..\include\system\system_time.h"

/**
 * @brief 判断闰年
 * @param nYear 待判断的年份
 * @return true为闰年，false为平年
 */
bool IsLeapYear(int year)
{
	return ((year%4==0)&&(year%100!=0))||(year%400==0);
}

/**
 * @brief 获取小数部分
 * @param n 待处理的数值
 * @return 小数部分的值
 */
double GetDecimal(double n)
{
	n-=(int)n;
	return n;
}

/**
 * @brief 取整函数
 * @param n 待取整的数值
 * @return 取整后的结果
 */
int GetInteger(double n)
{
	int a=(int)n;
	if (n<0)
	{
		double rm=GetDecimal(n);
		if (rm!=0)
		{
			a--;
		}
	}
	return a;
}									

/**
 * @brief 计算基准日
 * @param year 年份
 * @param month 月份
 * @param day 日期
 * @return 相对于基准的天数
 */
int JDNBaseDay(int year,int month,int day)
{
	int basedays=0;
	if (month<=2&&month>0){	year--;	month+=12;	}
	int x=year/100,y=0;
	if (year==1582)//格里历起始年
	{
		if (month==10 && day>=15)y=2-x+(int)(x/4);
		else if (month>10)y=2-x+(int)(x/4);
	}
	else if (year>1582)//实际上只有这部分
	{
		y=2-x+(int)(x/4);
	}
	basedays=(int)(365.25*(year+16))+36525*47+(int)(30.6001*(month+1))+day+y-2453069;
	return basedays;
}

/**
 * @brief 将小数日转换为公历日期
 * @param n 小数日，通常是节气相关的小数表示
 * @return 精确的公历日期结构体，包含年、月、日、时、分、秒、毫秒以及星期等信息
 */
LONGTIME GetDate(double n)//将节气小数日转公历
{
	LONGTIME lt;
	lt.year=0;
	lt.month=0;
	lt.day=0;
	lt.hour=0;
	lt.minute=0;
	lt.second=0;
	lt.milliSecond=0;
	lt.dayOfWeek=0;

	n+=0.5;
	n+=2451545;
	
	lt.dayOfWeek=((GetInteger)(n+1))%7;

	double A,F,D;
	//int x=2451545;
	// x+=2;
	// x*=10;
	// x+=4;
	// x*=10;
	// x+=5;
	// x*=10;
	// x+=1;
	// x*=10;
	// x+=5;
	// x*=10;
	// x+=4;
	// x*=10;
	// x+=5;

	A=(int)n;
	
	F=n-A;
	if (A>=2299161)
	{
		D=(int)((A-1867216.25)/36524.25);
		A+=1+D-(int)(D/4);
	}
	A+=1524;
	
	lt.year=(int)((double)(A-122.1)/365.25);
	D=A-(int)(365.25*lt.year);

	lt.year-=4716;
	
	lt.month=(int)(D/30.6001);
	
	lt.day=(int)(D-(int)(lt.month*30.6001));
	
	lt.month--;
	
	if (lt.month>12)lt.month-=12;//13 to 1
	if (lt.month<=2)lt.year++; //上一年的农历月

	F*=24;
	lt.hour=(int)F;
	F-=lt.hour;
	
	F*=60;
	lt.minute=(int)F;
	F-=lt.minute;
	
	F*=60;
	lt.second=(int)F;
	F-=lt.second;
	
	F*=1000;
	lt.milliSecond=(int)F;
	
	return lt;
}

/**
 * @brief 获取指定月份的天数
 * @param year 年份
 * @param month 月份
 * @param mode 模式标志(false为公历，true为农历)
 * @param bLeap 是否为闰月标志(仅农历模式有效)
 * @return 当月天数
 */
int GetDaysOfMonth(int year,int month,bool mode,bool bLeap)
{
	int date1_base_days=0,days_of_month=0;
	if (!mode)
	{
		date1_base_days=JDNBaseDay(year,month,1);				//月首天数
		if (month==12)days_of_month=JDNBaseDay(++year,1,1)-date1_base_days;			//元旦
		else days_of_month=JDNBaseDay(year,++month,1)-date1_base_days;		//下个月
	}
	else
	{
		int leap_Month=-1;						//农历闰月所在位置
		int row=year-START_YEAR;				//所在行
		int i=0;
		int col=month+1;
		for (i=0;i<14;i++)
		{
			if (Yuexu[row*14+i+1]==Yuexu[row*14+i])
			{
				leap_Month=i-1;
				break;
			}
		}
		if (leap_Month==-1)
		{
			if (bLeap)return -1;					//如果当前年份无闰月，而输入有闰月，则返回-1，以代表输入错误
		}else
		{
			if (bLeap && leap_Month!=month)return -1;		//当前年份有闰月，但并非当前输入月份
		}
		if (leap_Month!=-1)
		{
			if (month>leap_Month)		//在当年闰月以及之后
			{

				col++;
			}
			else
			{
				if (bLeap==true&&month==leap_Month)
				{
					col++;
				}
			}
		}
		days_of_month=Yueshou[row*15+col+1]-Yueshou[row*15+col];
	}
	return days_of_month;//返回当前公历月份的天数
}

/**
 * @brief 公历转农历
 * @param gre_date 输入的公历日期结构体，包含要转换的公历日期信息。
 * @param p_lunar_date 输出的农历日期结构体指针，用于存储转换后的农历日期信息。
 */
void GreToLunar(GREDATE gre_date,LUNARDATE *p_lunar_date)		
{
	int year=gre_date.year;
	int month=gre_date.month;
	int day=gre_date.day;

	int Lyear=0,Lmonth=0,Lday=0,LisLeapMonth=0,LdaysOfMonth=0;

	int base_days=0;			//基准日
	Lyear=year;
	base_days=JDNBaseDay(year,month,day);
	gre_date.baseDays=base_days;

	int row=year-START_YEAR;
	int col=month-1;

	//定位col在Yueshou数组中的位置
	for (;col<15;col++)
	{
		if ((Yueshou[row*15+col]>base_days) && (Yueshou[row*15+col-1]<=base_days))break;
	}
	col--;//减掉多加的1

	if (Yuexu[row*14+col-1]==Yuexu[row*14+col])LisLeapMonth=1;

	if ((Yueshou[row*15+col+1]-Yueshou[row*15+col])==31)LdaysOfMonth=30;
	else LdaysOfMonth=29;

	Lmonth=Yuexu[row*14+col]-1;
	
	if (Lmonth<1)Lmonth+=12;//代表12
	
	if (Lmonth>10&&((row*14+col)%14<2))Lyear--;//对于十一月和十二月的，年份应为上一年的

	Lday=base_days-Yueshou[row*15+col];//从初一开始

	p_lunar_date->year=Lyear;
	p_lunar_date->month=Lmonth;
	p_lunar_date->day=Lday;
	p_lunar_date->isLeapMonth=LisLeapMonth;
	p_lunar_date->daysOfMonth=LdaysOfMonth;
}

/**
 * @brief 农历转公历

 */
void LunarToGre(LUNARDATE lunar_date,GREDATE *p_gre_date)							
{
	int row=lunar_date.year-START_YEAR;
	int col=lunar_date.month+1;
	int leap_month=-1;
	int i=0;
	double ndays=0.0;
	LONGTIME lt;

	if(lunar_date.isLeapMonth)
	{
		for (i=0;i<14;i++)
		{
			if (Yuexu[row*14+i+1]==Yuexu[row*14+i])
			{
				leap_month=i-1;//农历闰月
				if (leap_month<=0)
				{
					leap_month+=12;
				}
				break;
			}
		}
	}

	if (leap_month!=-1)
	{
		//定位当前列
		if (lunar_date.month>leap_month)
		{
			col++;
		}
		else if (lunar_date.month==leap_month&&lunar_date.isLeapMonth==true)
		{
			col++;
		}
	}
	ndays=Yueshou[row*15+col];
	ndays+=lunar_date.day;
	ndays--;
	lt=GetDate(ndays);

	p_gre_date->year=lt.year;
	p_gre_date->month=lt.month;
	p_gre_date->day=lt.day;

}
	
/**
 * @brief 显示指定年月的公历和农历对照日历
 * @param gre_date 包含公历日期信息的结构体
 */
void ShowCalendar(GREDATE gre_date)
{
    // 提取公历日期信息
    int year = gre_date.year;
    int month = gre_date.month;
    //int day = gre_date.day;
    //int base_days = gre_date.baseDays;
    
    // 计算公历月份信息
    int days_in_month = GetDaysOfMonth(year, month, false, false);
    int first_day_of_month = JDNBaseDay(year, month, 1); // 当月第一天的基准日
    int first_weekday = (first_day_of_month + 142113) % 7; // 当月第一天是星期几(0=周日)
    
    // 查找对应的农历月份信息
    int table_row = year - START_YEAR; // 数据表中的行号
    int lunar_month_index = month - 1; // 从公历月推算农历月的起始索引
    
	
    // 查找公历月首对应的农历月
    while (lunar_month_index < 15) {
        if (Yueshou[table_row * 15 + lunar_month_index - 1] <= first_day_of_month && 
            Yueshou[table_row * 15 + lunar_month_index] > first_day_of_month) {
            break;
        }
        lunar_month_index++;
    }
    lunar_month_index--; // 修正索引值
    
    // 判断农历月份属性
    bool is_leap_month = (Yuexu[table_row * 14 + lunar_month_index - 1] == Yuexu[table_row * 14 + lunar_month_index]);
    int lunar_days = Yueshou[table_row * 15 + lunar_month_index + 1] - Yueshou[table_row * 15 + lunar_month_index];

    const char* month_type = (lunar_days == 30) ? "大" : "小";
    
    // 计算农历日期
    int lunar_year = year;
    int lunar_month_code = Yuexu[table_row * 14 + lunar_month_index] - 1;
    if (lunar_month_code < 1) lunar_month_code += 12;
    
    // 特殊处理农历十一月和十二月(可能属于上一年)
    if (lunar_month_code > 10 && ((table_row * 14 + lunar_month_index) % 14 < 2)) {
        lunar_year--;
    }
    
    // 计算当月第一天对应的农历日
    int lunar_day = first_day_of_month - Yueshou[table_row * 15 + lunar_month_index];
    
    // 计算节气信息
    int jieqi_index = (month - 1) * 2; // 初始节气索引（如1月对应0、1号节气）
    double jieqi1, jieqi2;
	for (jieqi_index=(month-1)*2;jieqi_index<24;jieqi_index++) {
    	int jq1=Jieqi[table_row*24+jieqi_index];
    	int jq2=Jieqi[table_row*24+jieqi_index+1];
    if (GetInteger(jq1+0.5)<=(first_day_of_month+14)&&GetInteger(jq2+0.5)>(first_day_of_month+14)) break;
	}
	jieqi1 = Jieqi[table_row * 24 + jieqi_index];
    jieqi2 = Jieqi[table_row * 24 + jieqi_index + 1];
    
    // 输出日历标题
    printf("\n\t\t公历  %d年%d月\n\n", year, month);
    printf(" 周日\t 周一\t 周二\t 周三\t 周四\t 周五\t 周六\n");
    printf("________________________________________________________\n");
    
    // 计算需要的行数
    int total_weeks = (days_in_month + first_weekday + 6) / 7;
    
    // 按行输出日历
    for (int week = 0; week < total_weeks; week++) {
        // 输出公历行
        for (int day_of_week = 0; day_of_week < 7; day_of_week++,SetConsoleColor(BLACK_TEXT,WHITE_BG)) {		
            int day_number = week * 7 + day_of_week - first_weekday + 1;
       
            if (day_number >= 1 && day_number <= days_in_month) 
			{
				if(day_of_week==0 || day_of_week==6)SetConsoleColor(RED_TEXT,WHITE_BG);
                printf(" %2d\t", day_number);
            }
			else 
			{
				if(day_number==0)printf("   \t");
				else printf("   \t   "); // 填充空白
            }
			SetConsoleColor(BLACK_TEXT,WHITE_BG);
        }
        printf("\n");
        
        // 输出农历行
        for (int day_of_week = 0; day_of_week < 7; day_of_week++,SetConsoleColor(BLACK_TEXT,WHITE_BG)) 
		{
            int day_number = week * 7 + day_of_week - first_weekday + 1;
            int current_jd = first_day_of_month + day_number - 1;
            
            if (day_number < 0 || day_number > days_in_month) {
                printf("   \t   "); // 填充空白
                continue;
            }
            else if(day_number==0)
			{
				printf("   \t");
				continue;
			}
            if(day_of_week==0 || day_of_week==6)SetConsoleColor(RED_TEXT,WHITE_BG);
			// 检查是否为节气
            if (GetInteger(jieqi1 + 0.5) == current_jd) {
                printf("%s\t", jieqi[jieqi_index % 24]);
            } 
			else if (GetInteger(jieqi2 + 0.5) == current_jd) {
                printf("%s\t", jieqi[(jieqi_index + 1) % 24]);
            } 
			else {
                //普通农历日期
                if (lunar_day == 0) {
                    // 农历月首日
					printf("%s", month_type);
                    if (is_leap_month) {
                        printf("闰%s", mName[lunar_month_code - 1]);
                    } else {
                        printf("%s\t", mName[lunar_month_code - 1]);
                    }               
                }
				else {
                    //农历月中
                    printf("%s\t", dName[lunar_day]);
                }
                
                
            }
			// 更新农历日期
                lunar_day++;
                if (lunar_day >= lunar_days) {
                    // 切换到下一个农历月
                    lunar_month_index++;
                    lunar_month_code = Yuexu[table_row * 14 + lunar_month_index] - 1;
                    if (lunar_month_code < 1) lunar_month_code += 12;
                    
                    // 检查是否需要调整农历年份
                    if (lunar_month_code > 10) {
                        lunar_year--;
                    }
                    
                    lunar_days = Yueshou[table_row * 15 + lunar_month_index + 1] - Yueshou[table_row * 15 + lunar_month_index];
                    lunar_day = 0;
                    is_leap_month = (Yuexu[table_row * 14 + lunar_month_index - 1] == Yuexu[table_row * 14 + lunar_month_index]);
                    month_type = (lunar_days == 30) ? "大" : "小";
                }
				
        }
        printf("\n\n");
		
    }
    
    printf("\r\n________________________________________________________\n");
}

/**
 * @brief 使用蔡氏公式计算星期几
 * @param year 年份
 * @param month 月份
 * @param day 日期
 * @return 计算得到的星期数，0表示星期日，1表示星期一，以此类推
 */
int ZellerWeek(int year,int month,int day)
{
    int m=month;
    int d=day;
    if(month<=2)
    {
        year--;
        m=month+12;
    }
    int y=year%100;
    int c=year/100;//centry-1
    int w=(y+y/4+c/4-2*c+(13*(m+1)/5)+d-1)%7;
    if(w<0)w+=7;
    return w;//week
}
